#include "../include/Scene.hpp"
#include "../include/Utils.hpp"
using namespace klas;

#include <iostream>
using namespace std;

Scene::Scene(const Vector2<Real>& dim, Real cellSize)
{
	// Setup internal variables.
	m_dimensions = dim;
	m_cellSize = cellSize;
	m_numCells.x = dim.x / cellSize;
	m_numCells.y = dim.y / cellSize;
	m_nextID = 0;
	
	// Create the cells.
	m_cells.reserve(m_numCells.x * m_numCells.y);
	for(Real y = 0; y < m_numCells.y; ++y)
		for(Real x = 0; x < m_numCells.x; ++x)
			m_cells.push_back(Cell(Rectangle<Real>
				(Vector2<Real>(x*cellSize, y*cellSize), 
					(Vector2<Real>(x*cellSize, y*cellSize) + dim))));
					
	m_fontNodes.clear();
}

void Scene::kill()
{
	for(vector<Cell>::iterator it = m_cells.begin(); it != m_cells.end(); ++it)
	{
		for(list<Node*>::iterator currNode = it->members.begin();
					currNode != it->members.end(); ++currNode)
		{
				delete(*currNode);
		}
	}
	
	m_cells.clear();
	
	for(list<Node*>::iterator it = m_fontNodes.begin(); it != m_fontNodes.end(); ++it)
		delete(*it);
		
	m_fontNodes.clear();

}

Node* Scene::add(Node* node, uint8 flags)
{
	Node* add = node;
	
	if((flags & DEEP_COPY) != 0)
		add = node->getDeepCopy();
		
	add->setID(m_nextID);
	++m_nextID;
	
	//cout << "index added to: " << posToIdx(add->getRect().getPos()) << endl;
	
	if(add->getNodeType() != Node::FONT)
	{
		if((flags & RENDER_BACK) != 0)
			m_cells[posToIdx(add->getRect().getPos())].members.push_back(add);
		else
			m_cells[posToIdx(add->getRect().getPos())].members.push_front(add);
	}
	else
		m_fontNodes.push_back(add);

  // Add the node's polygon
  /*Vector2<Real> points;
  points.push_back(Vector2<Real>(node->getRect().x1, node->getRect().y1));
  points.push_back(Vector2<Real>(node->getRect().x1, node->getRect().y2));
  points.push_back(Vector2<Real>(node->getRect().x2, node->getRect().y1));
  points.push_back(Vector2<Real>(node->getRect().x2, node->getRect().y2));
  Polygon* poly = new Polygon(points);
  m_cells[posToIdx(add->getRect().getPosA())].polygons.push_back(poly);*/
	
	return add;
	
}

bool Scene::update(Node* node)
{
	// Get the indexes of the nodes.
	Real oldIdx = posToIdx(node->getPrevRect().getPos());
	Real newIdx = posToIdx(node->getRect().getPos());
	
	// If the old index of the node is the same as the new one, then return false.
	if(oldIdx == newIdx)
		return false;
		
	// Remove the node from the old cell and add it to the new one.
	m_cells[oldIdx].members.remove(node);
	m_cells[newIdx].members.push_back(node);
	
	// The node switched cells, so return true.
	return true;
}

Node* Scene::get(Real ID)
{
	// We iterate all the nodes.
	for(vector<Cell>::const_iterator it = m_cells.begin(); it != m_cells.end(); ++it)
	{
		for(list<Node*>::const_iterator currNode = it->members.begin();
					currNode != it->members.end(); ++it)
			// If the current node's ID matches our target ID, then return that node.
			if((*currNode)->getID() == ID)
				return (*currNode);
	}
	
	return (Node*)(NULL);
}

list<Node*> Scene::getIntersectingNodes(const Rectangle<Real>& rect)
{
	/*{
	list<Node*> rtn;
	for(vector<Cell>::iterator it = m_cells.begin(); it != m_cells.end(); ++it)
		for(list<Node*>::iterator currNode = it->members.begin();
				currNode != it->members.end(); ++currNode)
		{
			rtn.push_back(*currNode);
			//cout << "adding a node" << endl;
		}
		
	return rtn;
	}
	
	
	/* Algorithm:
				Calculate intersecting cells.
				Iterate instersecting cells' nodes and find the ones that intersect w/ rect.
				Return those nodes. */
	
	list<Cell*> checkAll, // We are not sure that all of the nodes in this cell 
												// intersect with the rect.
							addAll;		// All of the nodes in the cell intersect with the rect.
							
	Vector2<iReal> numCells(rect.w / m_cellSize, rect.h / m_cellSize);
	
	Vector2<Real> currPos = rect.getPos();
	
	if(numCells.x < 1) numCells.x = 1;
	if(numCells.y < 1) numCells.y = 1;
	
	//cout << numCells.x << " " << numCells.y << endl;
	
	// -------------------------------------------------
	// Finding intersecting cells ----------------------
	
	// Iterate all cells that the rect contains	
	for(iReal y = 0; y < numCells.y; ++y)
	{
		for(iReal x = 0; x < numCells.x; ++x)
		{
			// If we are on the edges of the rect then we have to check all the nodes.
			if(x == 0 || x == numCells.x - 1 || y == 0 || y == numCells.y - 1)
				checkAll.push_back(&(m_cells[posToIdx(currPos)]));
			
			// Otherwise, we know that all the nodes in this cell are within the rect.
			else
				addAll.push_back(&(m_cells[posToIdx(currPos)]));
			
			// Go to the next cell.
			currPos.x += m_cellSize;
		}
		
		// Reset the x and go down a level.
		currPos.x = rect.x;
		currPos.y += m_cellSize;
	}
	
	// -------------------------------------------------
	// Iterating intersecting cells --------------------
	
	// The nodes we are going to return.
	list<Node*> nodes;
	
	/* WARNING: WE HAVE SKIPPED CHECKING */
	
	// We need to check all of the nodes inside of cells that we are not confident
	// that all of the nodes intersect the rect.
	for(list<Cell*>::const_iterator currCell = checkAll.begin(); 
			currCell != checkAll.end(); ++currCell)
		for(list<Node*>::const_iterator currNode = (*currCell)->members.begin(); 
				currNode != (*currCell)->members.end(); ++currNode)
			//if(Utils::intersects((*currNode)->getRect(), (*currCell)->rect))
			{
				nodes.push_back(*currNode);
			}
	
	// Iterate all nodes in cells we are sure of and add them to our nodes to return.
	for(list<Cell*>::const_iterator currCell = addAll.begin();
			currCell != addAll.end(); ++currCell)
		for(list<Node*>::const_iterator currNode = (*currCell)->members.begin();
				currNode != (*currCell)->members.end(); ++currNode)
		{
			nodes.push_back(*currNode);
		}
			
			
	// Finally, we return our list of nodes that are intersecting the box.
	return nodes;
}

void Scene::render(const Rectangle<Real>& screen)
{
	list<Node*> nodes = getIntersectingNodes(screen);
	for(list<Node*>::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
		(*it)->render(screen);
}

void Scene::renderFonts()
{
	Rectangle<Real> screen(0, 0, 0, 0);
	for(list<Node*>::const_iterator it = m_fontNodes.begin(); it != m_fontNodes.end();
				++it)
		(*it)->render(screen);
}

Scene::Cell::Cell(const Rectangle<Real>& rect)
{
	this->rect = rect;
}

iReal Scene::posToIdx(const Vector2<Real>& pos)
{
	/*iReal idx = iReal(m_numCells.x * pos.x / m_dimensions.x) +
								iReal((m_numCells.y * pos.y / m_dimensions.y) * m_numCells.x);

	// If the entity's position is equal to Vector2(m_cellSize.x, m_cellSize.y)
  // then the index will overshoot; We need to check for this and adjust.
  if (idx > iReal(m_cells.size()-1))
  	idx = iReal(m_cells.size()-1);

  return idx;*/
  
  return 0;
}








bool Scene::remove(Node* node)
{
  int idx = posToIdx(node->getPos());
  
  m_cells[idx].members.remove(node);
  //delete(node);
  
  return true;
}




void Scene::updateCollisionState()
{




}


bool Scene::isCollidingWith(Node* a, Node* b)
{
  return a->getPolygon().intersects(b->getPolygon());
}





















